package model.components.networks.network;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;

import edu.uci.ics.jung.graph.Graph;

import model.components.graphs.graph.SparseMultigraphGraph;
import model.components.links.comparator.LinkNumberComparator;
import model.components.links.link.Link;
import model.components.links.link.VirtualLink;
import model.components.nodes.comparator.NodeNumberComparator;
import model.components.nodes.node.VirtualNode;
import model.components.nodes.node.PhysicalNode;
import model.components.nodes.node.Node;
import model.components.nodes.node.VirtualRouter;
import model.components.nodes.node.VirtualServer;

public class VirtualNetwork extends Network
{	
	private int startingDate;
	public int getStartingDate(){ return startingDate; }
	public void setStartingDate( int startingDate ){ this.startingDate = startingDate; }
	
	private int endingDate;
	public int getEndingDate(){ return endingDate; }
	public void setEndingDate( int endingDate ){ this.endingDate = endingDate; }
	
	private String physicalNetworkName = "";
	public String getPhysicalNetworkName(){ return physicalNetworkName; }
	public void setPhysicalNetworkName( String physicalNetworkName ){ this.physicalNetworkName = physicalNetworkName; }
	
	private List< VirtualNetwork > virtualSubnetworks;
	public List< VirtualNetwork > getVirtualSubnetworks(){ return virtualSubnetworks; }
	public void setVirtualSubnetworks( List< VirtualNetwork > virtualSubnetworks ){ this.virtualSubnetworks = virtualSubnetworks; }
	
	private LinkedHashMap< VirtualNode, PhysicalNode > virtualNodesMapping;
	public LinkedHashMap< VirtualNode, PhysicalNode > getVirtualNodesMapping(){ return virtualNodesMapping; }
	public void setVirtualNodesMapping( LinkedHashMap< VirtualNode, PhysicalNode > virtualNodesMapping ){ this.virtualNodesMapping = virtualNodesMapping; }
	
	private int virtualLinksMapping[][][][];
	public int[][][][] getVirtualLinksMapping(){ return virtualLinksMapping; }
	public void setVirtualLinksMapping( int virtualLinksMapping[][][][] ){ this.virtualLinksMapping = virtualLinksMapping; }
	
	private boolean denied;
	public boolean isDenied(){ return denied; }
	public void setDenied( boolean denied ){ this.denied = denied; }
	
	private int networkCost;
	public int getNetworkCost(){ return networkCost; }
	public void setNetworkCost( int networkCost ){ this.networkCost = networkCost; }
	
	public VirtualNetwork( String namePrefix, int number, int startingDate, int endingDate )
    {
    	super( namePrefix, number, new SparseMultigraphGraph() );
    	
    	this.setStartingDate( startingDate );
		this.setEndingDate( endingDate );
    }
	
	public VirtualNetwork( String namePrefix, int number, Graph< Node, Link > graph, int startingDate, int endingDate )
    {
    	super( namePrefix, number, graph );
    	
    	this.setStartingDate( startingDate );
		this.setEndingDate( endingDate );
    }
	
	public VirtualNetwork getCopy() 
	{
		VirtualNetwork virtualNetwork = new VirtualNetwork( this.getNamePrefix(), this.getNumber(), ( (SparseMultigraphGraph) this.getGraph() ).getCopy(), this.getStartingDate(), this.getEndingDate() );
		virtualNetwork.setState( this.getState() );
    	virtualNetwork.setChange( this.isChange() );
    	virtualNetwork.setTimeStamp( this.getTimeStamp() );
    	
    	return virtualNetwork;
    }
	
	public List< VirtualNode > getVirtualNodes()
	{
		List< VirtualNode > virtualNodes = new ArrayList< VirtualNode >();
		
		for( Node node: this.getNodes() )
			virtualNodes.add( ( VirtualNode )node );
		
		Collections.sort( virtualNodes, new NodeNumberComparator() );
		
		return virtualNodes;
	}
	
	public double[][] getNodesCapacities()
	{
		double nodesCapacities[][] = new double[ 3 ][ this.getNodes().size() ];

		for( int i = 0; i < this.getVirtualNodes().size(); i++ )
		{
			VirtualNode requestNode = this.getVirtualNodes().get( i );

			nodesCapacities[ 0 ][ i ] = requestNode.getCpu();
			nodesCapacities[ 1 ][ i ] = requestNode.getMemory();
			
			if( requestNode instanceof VirtualServer )
				nodesCapacities[ 2 ][ i ] = requestNode.getDiskSpace();
		}
		
		return nodesCapacities;
	}
	
	public List< VirtualLink > getVirtualLinks()
    {
    	List< VirtualLink > links = new ArrayList< VirtualLink >();
		
		for( Link link: this.getLinks() )
			links.add( ( VirtualLink )link );
		
		Collections.sort( links, new LinkNumberComparator() );
    	
    	return links;
	}
	
	public double[][] getBandwidths()
	{
		double bandwidths[][] = new double[ this.getNodes().size() ][ this.getNodes().size() ];
		
		for( VirtualLink linkRequest: this.getVirtualLinks() )
		{
			bandwidths[ linkRequest.getFirstNode().getNumber()  ][ linkRequest.getSecondNode().getNumber() ] = linkRequest.getBandwidth();
			bandwidths[ linkRequest.getSecondNode().getNumber() ][ linkRequest.getFirstNode().getNumber()  ] = linkRequest.getBandwidth();
		}
		
		return bandwidths;
	}
	
	public double revenueMapping()
	{
		double revenueMapping = 0;
	
		for( VirtualNode nodeRequest: this.getVirtualNodes() )
		{
			if( nodeRequest instanceof VirtualServer )
				revenueMapping += nodeRequest.getCpu() + nodeRequest.getMemory() + nodeRequest.getDiskSpace();
			else if( nodeRequest instanceof VirtualRouter )
				revenueMapping += 30;
		}
		
		for( VirtualLink linkRequest: this.getVirtualLinks() )
			revenueMapping += linkRequest.getBandwidth();
		
		return revenueMapping;
	}
	
	public double costMapping( List< PhysicalNetwork > networksSubstrateSimulation, int numberHopsNetworkSubstrateConnect[][] )
	{
		double costMapping = 0;
		
		for( VirtualNetwork subnetworkRequest: this.getVirtualSubnetworks() )
		{
			int mappingLinksRequest[][][][] = subnetworkRequest.getVirtualLinksMapping();
			
			if( mappingLinksRequest != null )
				for( int i = 0; i < mappingLinksRequest.length; i++ )
					for( int j = 0; j < mappingLinksRequest[ i ].length; j++ )
						for( int k = 0; k < mappingLinksRequest[ i ][ j ].length; k++ )
							for( int l = 0; l < mappingLinksRequest[ i ][ j ][ k ].length; l++ )
								costMapping += mappingLinksRequest[ i ][ j ][ k ][ l ];
		}
		
		for( VirtualNode nodeRequest: this.getVirtualNodes() )
			if( nodeRequest instanceof VirtualServer )
				costMapping += nodeRequest.getCpu() + nodeRequest.getMemory() + nodeRequest.getDiskSpace();
		
		if( this.getVirtualSubnetworks() != null )
			for( VirtualLink linkRequest: this.getVirtualLinks() )
			{
				String nameNetworkNodeFirstSubstrate = ( (VirtualNode) linkRequest.getFirstNode() ).getPhysicalNode().getNetworkName();
				String nameNetworkNodeSecondSubstrate = ( (VirtualNode) linkRequest.getSecondNode() ).getPhysicalNode().getNetworkName();
				
				if( !nameNetworkNodeFirstSubstrate.equals( nameNetworkNodeSecondSubstrate ) )
				{
					int iNetworkSubstrateFirst = 0;
					
					for( PhysicalNetwork physicalNetwork: networksSubstrateSimulation )
						if( physicalNetwork.getName().equals( nameNetworkNodeFirstSubstrate ) )
						{
							iNetworkSubstrateFirst = physicalNetwork.getNumber();
							break;
						}
					
					int iNetworkSubstrateSecond = 0;
					
					for( PhysicalNetwork physicalNetwork: networksSubstrateSimulation )
						if( physicalNetwork.getName().equals( nameNetworkNodeSecondSubstrate ) )
						{
							iNetworkSubstrateSecond = physicalNetwork.getNumber();
							break;
						}
					
					costMapping += linkRequest.getBandwidth()* numberHopsNetworkSubstrateConnect[ iNetworkSubstrateFirst ][ iNetworkSubstrateSecond ];
				}
			}
		
		return costMapping;
	}
	
	public double numberOfHopsMapping( List< PhysicalNetwork > networksSubstrateSimulation, int numberHopsNetworkSubstrateConnect[][] )
	{
		double numberOfHopsMapping = 0;
		
		for( VirtualNetwork subnetworkRequest: this.getVirtualSubnetworks() )
		{
			int mappingLinksRequest[][][][] = subnetworkRequest.getVirtualLinksMapping();
			
			if( mappingLinksRequest != null )
				for( int i = 0; i < mappingLinksRequest.length; i++ )
					for( int j = 0; j < mappingLinksRequest[ i ].length; j++ )
						for( int k = 0; k < mappingLinksRequest[ i ][ j ].length; k++ )
							for( int l = 0; l < mappingLinksRequest[ i ][ j ][ k ].length; l++ )
								if( mappingLinksRequest[ i ][ j ][ k ][ l ] > 0 )
									numberOfHopsMapping++;
		}
		
		if( this.getVirtualSubnetworks() != null )
			for( VirtualLink linkRequest: this.getVirtualLinks() )
			{
				String nameNetworkNodeFirstSubstrate = ( (VirtualNode) linkRequest.getFirstNode() ).getPhysicalNode().getNetworkName();
				String nameNetworkNodeSecondSubstrate = ( (VirtualNode) linkRequest.getSecondNode() ).getPhysicalNode().getNetworkName();
				
				if( !nameNetworkNodeFirstSubstrate.equals( nameNetworkNodeSecondSubstrate ) )
				{
					int iNetworkSubstrateFirst = 0;
					
					for( PhysicalNetwork physicalNetwork: networksSubstrateSimulation )
						if( physicalNetwork.getName().equals( nameNetworkNodeFirstSubstrate ) )
						{
							iNetworkSubstrateFirst = physicalNetwork.getNumber();
							break;
						}
					
					int iNetworkSubstrateSecond = 0;
					
					for( PhysicalNetwork physicalNetwork: networksSubstrateSimulation )
						if( physicalNetwork.getName().equals( nameNetworkNodeSecondSubstrate ) )
						{
							iNetworkSubstrateSecond = physicalNetwork.getNumber();
							break;
						}
					
					numberOfHopsMapping += numberHopsNetworkSubstrateConnect[ iNetworkSubstrateFirst ][ iNetworkSubstrateSecond ];
				}
			}
		
		return numberOfHopsMapping;
	}
	
}
